/***************************** BEGIN LICENSE BLOCK ***************************

 The contents of this file are subject to the Mozilla Public License Version
 1.1 (the "License"); you may not use this file except in compliance with
 the License. You may obtain a copy of the License at
 http://www.mozilla.org/MPL/MPL-1.1.html
 
 Software distributed under the License is distributed on an "AS IS" basis,
 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 for the specific language governing rights and limitations under the License.
 
 The Original Code is the "EO-SPS Framework".
 
 The Initial Developer of the Original Code is Spotimage S.A.
 Portions created by the Initial Developer are Copyright (C) 2007
 the Initial Developer. All Rights Reserved.
 
 Contributor(s): 
    Alexandre Robin <alexandre.robin@spotimage.fr>
 
******************************* END LICENSE BLOCK ***************************/

package com.spotimage.eosps;

import java.text.ParseException;
import java.util.List;
import org.vast.ows.sps.StatusReport;
import org.vast.ows.sps.TaskingResponse;
import org.vast.ows.swe.SWESUtils;
import org.vast.util.DateTime;
import org.vast.xml.QName;
import org.w3c.dom.Element;
import com.vividsolutions.jts.geom.Polygon;


/**
 * <p><b>Title:</b><br/>
 * EO Report Helper
 * </p>
 *
 * <p><b>Description:</b><br/>
 * Helper to get and set extended info specific to spaceborne earth
 * observation instruments from/to SPS responses. More specific helpers
 * are also available for optical and SAR instruments.<br/>
 * This class must be constructed from an existing GetFeasibility,
 * Submit, GetStatus, Update, Reserve or Confirm response (for instance
 * as obtained using the generic SPS deserializers).
 * 
 * TODO implement unit conversion in all get/set methods dealing with 
 * decimal values
 * 
 * </p>
 *
 * <p>Copyright (c) 2008, Spot Image</p>
 * @author Alexandre Robin <alexandre.robin@spotimage.fr>
 * @date Dec, 18th 2008
 * @since 1.0
 */
public class EOReportHelper implements EOConstants
{
	protected static String EO_REPORT_OBJECT = "EO_REPORT";
	
	protected StatusReport spsReport;
	protected ReportType reportType;
	protected EOReportInfo eoReport;
	protected EOReportObject selectedObject;
	protected Segment selectedSegment;
	protected GridCell selectedCell;
	
	
	/**
	 * Constructor using a report that already contains feasibility study
	 * or programming status extended information, either in the form of
	 * an XML DOM tree or EOReportInfo object
	 * @param report
	 * @param reportType
	 */
	public EOReportHelper(StatusReport report)
	{
		this.spsReport = report;
		this.reportType = null;
		
		// try to retrieve feasibility report extension as DOM element
		Element reportElt = (Element)SWESUtils.findExtension(EOT_NAMESPACE, FEASIBILITY_STUDY_ELT, report.getExtensions());
		this.reportType = ReportType.FEASIBILITY;
		
		// if non was found try to retrieve prog status report as DOM element
		if (reportElt == null)
		{
			reportElt = (Element)SWESUtils.findExtension(EOT_NAMESPACE, PROGRAMMING_STATUS_ELT, report.getExtensions());
			this.reportType = ReportType.PROG_STATUS;
		}
		
		// parse report and create proper EOReportInfo object
		try
		{
			if (reportElt != null)
			{
				EOReportReader reader = new EOReportReader();
				eoReport = reader.parseReport(reportElt);
			}
			else
				this.reportType = null;
		}
		catch (ParseException e)
		{
			e.printStackTrace();
		}
	}
	
	
	/**
	 * Constructor using a response that already contains feasibility study
	 * or programming status extended information (for reading EO metadata)
	 * @param response TaskingResponse response object containing report data
	 */
	public EOReportHelper(TaskingResponse response)
	{
		this(response.getReport());
	}
		
	
	/**
	 * Constructor using a report without extended info (for writing EO metadata)
	 * @param report Report object to which extended EO information will be appended
	 * @param reportType Type of EO extended info to add to the SPS report
	 */
	public EOReportHelper(StatusReport report, ReportType reportType)
	{
		this.spsReport = report;
		
		// check if EOReportInfo object is already there
		eoReport = (EOReportInfo)SWESUtils.findExtension(EOT_NAMESPACE, EO_REPORT_OBJECT, report.getExtensions());
		if (eoReport != null)
		{
			if (eoReport instanceof FeasibilityStudy)
				this.reportType = ReportType.FEASIBILITY;
			else if (eoReport instanceof ProgrammingStatus)
				this.reportType = ReportType.PROG_STATUS;			
			return;
		}
		
		// otherwise create a new one
		this.reportType = reportType;		
		switch (reportType)
		{
			case PROG_STATUS:
				eoReport = new ProgrammingStatus();
				break;
				
			case FEASIBILITY:
				eoReport = new FeasibilityStudy();
				break;
		}
		
		report.getExtensions().put(new QName(EOT_NAMESPACE, EO_REPORT_OBJECT), eoReport);
	}
	
	
	/**
	 * Constructor using a response without extended data
	 * and the report component structure (for writing EO metadata)
	 * @param response TaskingResponse response object receiving report data
	 * @param reportParams Type of EO extended info to add to the SPS response
	 */
	public EOReportHelper(TaskingResponse response, ReportType reportType)
	{
		this(response.getReport(), reportType);
	}
	
	
	/**
	 * Retrieve the type of information that is contain in the
	 * status report (i.e. feasibility or programming status) 
	 * @return
	 */
	public ReportType getReportType()
	{
		return reportType;
	}
	
	
	/**
	 * Checks if the report contains a list of segments
	 * @return
	 */
	public boolean hasSegments()
	{
		return (!eoReport.getSegments().isEmpty());
	}
	
	
	/**
	 * Checks if the report contains a list of grid cells
	 * @return
	 */
	public boolean hasGridCells()
	{
		return (!eoReport.getCells().isEmpty());
	}


	/**
	 * Sets the total number of segments to be added to the report
	 * @param numSegments
	 */
	public void setNumSegments(int numSegments)
	{
		for (int i=0; i<numSegments; i++)
			eoReport.getSegments().add(new Segment());
	}
	
	
	/**
	 * Retrieves the number of segments included in this report
	 * This needs to be called before adding any segment data
	 * @return
	 */
	public int getNumSegments()
	{
		return eoReport.getSegments().size();
	}
	
	
	/**
	 * Sets the total number of grid cells to be added to the report
	 * This needs to be called before adding any grid cell data
	 * @param numCells
	 */
	public void setNumGridCells(int numCells)
	{
		for (int i=0; i<numCells; i++)
			eoReport.getCells().add(new GridCell());
	}
	
	
	/**
	 * Retrieves the number of grid cells included in this report
	 * @return
	 */
	public int getNumGridCells()
	{
		return eoReport.getCells().size();
	}
	

	/**
	 * Prepare the helper to load or read data from the specified segment
	 * This or addSegment needs to be called before adding data to this segment
	 * @param index
	 */
	public void loadSegment(int index)
	{
		List<Segment> segmentList = eoReport.getSegments();
		if (segmentList.get(index) == null)
			selectedSegment = segmentList.set(index, new Segment());
		else
			selectedSegment = segmentList.get(index);
		selectedObject = selectedSegment;
	}
	
	
	/**
	 * Adds a segment slot to the list of segments and prepare to set its properties 
	 * This or loadSegment needs to be called before adding data to this segment
	 */
	public void addSegment()
	{
		selectedObject = selectedSegment = new Segment();
		eoReport.getSegments().add(selectedSegment);		
	}
	
	
	/**
	 * Prepare the helper to load or read data from the specified grid cell
	 * This needs to be called before adding data to this grid cell
	 * @param index
	 */
	public void loadGridCell(int index)
	{
		List<GridCell> cellList = eoReport.getCells();
		if (cellList.size()-1 < index || cellList.get(index) == null)
			selectedCell = cellList.set(index, new GridCell());
		else
			selectedCell = cellList.get(index);
		selectedObject = selectedCell;
	}
	
	
	/**
	 * Adds a cell slot to the list of cells and prepare to set its properties 
	 * This or loadGridCell needs to be called before adding data to this cell
	 */
	public void addGridCell()
	{
		selectedObject = selectedCell = new GridCell();
		eoReport.getCells().add(selectedCell);
	}


	public String getID()
	{
		assert (selectedObject != null);
		return selectedObject.getId();
	}

	
	public void setID(String id)
	{
		assert (selectedObject != null);
		selectedObject.setId(id);
	}
	
	
	/**
	 * @deprecated
	 * @return
	 */
	public String getStatus()
	{
		assert (selectedObject != null);
		return selectedObject.getStatus().name();
	}
	
	
	/**
	 * @deprecated
	 * @param statusCode
	 */
	public void setStatus(String statusCode)
	{
		assert (selectedObject != null);
		selectedObject.setStatus(AcqStatus.valueOf(statusCode));
	}
	
	
	public AcqStatus getObjectStatus()
	{
		assert (selectedObject != null);
		return selectedObject.getStatus();
	}
	
	
	public void setObjectStatus(AcqStatus statusCode)
	{
		assert (selectedObject != null);
		selectedObject.setStatus(statusCode);
	}
	
	
	public Polygon getFootprint()
	{
		assert (selectedObject != null);
		return selectedObject.getFootprint();
	}
	

	public void setFootprint(Polygon footprint)
	{
		assert (selectedObject != null);
		selectedObject.setFootprint(footprint);
	}
	
	
	public double getSuccessRate()
	{
		assert (selectedObject != null);
		return selectedObject.getSuccessRate();
	}
	
	
	public void setSuccessRate(double successRate)
	{
		assert (selectedObject != null);
		selectedObject.setSuccessRate((float)successRate);		
	}
	
	
	/// Segment fields

	public String getPlatformID()
	{
		assert (selectedSegment != null);
		return selectedSegment.getPlatformID();
	}

	
	public void setPlatformID(String id)
	{
		assert (selectedSegment != null);
		selectedSegment.setPlatformID(id);
	}
	
	
	public String getPlatformName()
	{
		assert (selectedSegment != null);
		return selectedSegment.getPlatformName();
	}

	
	public void setPlatformName(String name)
	{
		assert (selectedSegment != null);
		selectedSegment.setPlatformName(name);
	}


	public String getInstrumentID()
	{
		assert (selectedSegment != null);
		return selectedSegment.getInstrumentID();
	}

	
	public void setInstrumentID(String id)
	{
		assert (selectedSegment != null);
		selectedSegment.setInstrumentID(id);
	}
	
	
	public String getInstrumentName()
	{
		assert (selectedSegment != null);
		return selectedSegment.getInstrumentName();
	}

	
	public void setInstrumentName(String name)
	{
		assert (selectedSegment != null);
		selectedSegment.setInstrumentName(name);
	}


	public String getInstrumentMode()
	{
		assert (selectedSegment != null);
		return selectedSegment.getInstrumentMode();
	}
	

	public void setInstrumentMode(String mode)
	{
		assert (selectedSegment != null);
		selectedSegment.setInstrumentMode(mode);
	}


	public DateTime getAcquisitionStart()
	{
		assert (selectedSegment != null);
		return selectedSegment.getAcquisitionStartTime();
	}
	
	
	public void setAcquisitionStart(DateTime startDate)
	{
		assert (selectedSegment != null);
		selectedSegment.setAcquisitionStartTime(startDate);
	}	
	
	
	public DateTime getAcquisitionStop()
	{
		assert (selectedSegment != null);
		return selectedSegment.getAcquisitionStopTime();
	}
	

	public void setAcquisitionStop(DateTime stopDate)
	{
		assert (selectedSegment != null);
		selectedSegment.setAcquisitionStopTime(stopDate);
	}

	
	public double getElevationIncidence()
	{
		assert (selectedSegment != null);
		return selectedSegment.getIncidenceAngle();
	}
	

	public void setElevationIncidence(double incidence)
	{
		assert (selectedSegment != null);
		selectedSegment.setIncidenceAngle((float)incidence);
	}
	
	
	/**
	 * @deprecated
	 * @return
	 */
	public double getAzimuthIncidence()
	{
		return Double.NaN;
	}
	

	/**
	 * @deprecated
	 * @return
	 */
	public void setAzimuthIncidence(double incidence)
	{
	}

	
	public int getOrbitNumber()
	{
		return 0;
	}	
	
	
	public void setOrbitNumber(int orbitNumber)
	{
				
	}
	
	
	public int getTrackNumber()
	{
		return 0;
	}
	
	
	public void setTrackNumber(int trackNumber)
	{
		
	}
	
	
	public int getFrameNumber()
	{
		return 0;
	}	
	
	
	public void setFrameNumber(int frameNumber)
	{
		
	}
	
	
	/**
	 * @deprecated
	 */
	public String getStereoSegmentID()
	{
		assert (selectedSegment != null);
		
		if (selectedSegment.getRelatedSegments().isEmpty())
			return null;
		else
			return selectedSegment.getRelatedSegments().get(0).getId();
	}
	

	/**
	 * @deprecated
	 */
	public void setStereoSegmentID(String stereoID)
	{
		// TODO 
	}
	
	
	public List<Segment> getRelatedSegments()
	{
		assert (selectedSegment != null);
		return selectedSegment.getRelatedSegments();
	}
	

	public void addRelatedSegment(Segment seg)
	{
		assert (selectedSegment != null);
		selectedSegment.getRelatedSegments().add(seg);
	}
	
	
	/**
	 * @deprecated Preview URL must be given in DescribeResultAccess response
	 * @return
	 */
	public String getPreviewUrl()
	{
		return null;
	}
	
	
	/**
	 * @deprecated Preview URL must be given in DescribeResultAccess response
	 * @return
	 */
	public void setPreviewUrl(String previewUrl)
	{
	}
	
	
	/// GridCell fields	
	
	public DateTime getEstimatedSuccessDate()
	{
		assert (selectedCell != null);
		return selectedCell.getEstimatedSuccessDate();
	}
	

	public void setEstimatedSuccessDate(DateTime date)
	{
		assert (selectedCell != null);
		selectedCell.setEstimatedSuccessDate(date);
	}
	
	
	public DateTime getLastAttemptDate()
	{
		assert (selectedCell != null);
		return selectedCell.getLastAttemptDate();
	}
	

	public void setLastAttemptDate(DateTime date)
	{
		assert (selectedCell != null);
		selectedCell.setLastAttemptDate(date);
	}
	
	
	public DateTime getNextAttemptDate()
	{
		assert (selectedCell != null);
		return selectedCell.getNextAttemptDate();
	}
	

	public void setNextAttemptDate(DateTime date)
	{
		assert (selectedCell != null);
		selectedCell.setNextAttemptDate(date);
	}
	
	
	public int getRemainingAttempts()
	{
		assert (selectedCell != null);
		return selectedCell.getRemainingAttempts();
	}	
	
	
	public void setRemainingAttempts(int remainingAttempts)
	{
		assert (selectedCell != null);
		selectedCell.setRemainingAttempts(remainingAttempts);
	}
		
	
	public double getPercentCompletion()
	{
		assert (eoReport instanceof ProgrammingStatus);
		return ((ProgrammingStatus)eoReport).getPercentCompletion();
	}
	

	public void setPercentCompletion(double percentComplete)
	{
		assert (eoReport instanceof ProgrammingStatus);
		((ProgrammingStatus)eoReport).setPercentCompletion((float)percentComplete);
	}
	
	
	public void setExpirationDelay(int seconds)
	{
		assert (eoReport instanceof FeasibilityStudy);
		((FeasibilityStudy)eoReport).setExpirationDelay(seconds);
	}
	
	
	public DateTime getExpirationDate()
	{
		assert (eoReport instanceof FeasibilityStudy);
		return ((FeasibilityStudy)eoReport).getExpirationDate();
	}
	
	
	public void setExpirationDate(DateTime date)
	{
		assert (eoReport instanceof FeasibilityStudy);
		((FeasibilityStudy)eoReport).setExpirationDate(date);
	}
	
	
	public double getEstimatedCost()
	{
		assert (eoReport instanceof FeasibilityStudy);
		return ((FeasibilityStudy)eoReport).getEstimatedCost();
	}
	
	
	public void setEstimatedCost(double cost)
	{
		assert (eoReport instanceof FeasibilityStudy);
		((FeasibilityStudy)eoReport).setEstimatedCost((float)cost);
	}
	
	
	public double getOverallSuccessRate()
	{
		assert (eoReport instanceof FeasibilityStudy);
		return ((FeasibilityStudy)eoReport).getSuccessRate();
	}
	
	
	public void setOverallSuccessRate(double successRate)
	{
		assert (eoReport instanceof FeasibilityStudy);
		((FeasibilityStudy)eoReport).setSuccessRate((float)successRate);		
	}
	
	
	public List<InformationType> getInformationUsed()
	{
		assert (eoReport instanceof FeasibilityStudy);
		return ((FeasibilityStudy)eoReport).getInformationUsed();
	}
	
	
	public void addInformationUsed(InformationType info)
	{
		assert (eoReport instanceof FeasibilityStudy);
		((FeasibilityStudy)eoReport).getInformationUsed().add(info);
	}
	
	
	public void writeReportInfo()
	{
		EOReportWriter writer = new EOReportWriter();
		Element reportElt = writer.writeReport(eoReport);
		spsReport.addExtension(reportElt);
	}
}